(仮) NextJS モノレポ運用


title: "(仮) NextJS モノレポ運用"
emoji: "💬"
type: "tech" # tech: 技術記事 / idea: アイデア
topics: ["nextjs", "serverless", "monorepo"]
published: true

これは ランサーズ Advent Calendar 2021 4 日目の記事です。

個人の開発では1レポジトリで完結するようなプロジェクトをよく作成します。

ただし実際の事業はスケールしたり、プロジェクトの中でも他のプロジェクトにも共有したいリソースというのは出るものなので初期段階でもしっかりとした基盤を事前に用意したいと思ったので作成しました。

プロジェクト構成

上記のようなルートプロジェクトにサブプロジェクトを git submodule で分割して管理しているような構成にしてます。
基本はGitなので対象のサブプロジェクト同士で異なるブランチでの検証も可能だったりします。
一部 git submodule管理されていないサブプロジェクトもありますが。

次にそれぞれのプロジェクトの説明に入ります。

ルートプロジェクト: syonet_eight

サブプロジェクトよりも上の階層にある大元のプロジェクトになります。

yarn workspace

NodeJSのライブラリをルートプロジェクトに集中管理し、 yarn workspace によるモノレポな環境にしています。
ルートプロジェクトにライブラリ管理を集中化させてしまってますが、サブプロジェクト側のpackage.jsonにバージョンの異なるライブラリを管理させたり、部分的に使用したいライブラリの管理ができるようになっているんじゃないかなと思います。

npm scripts などもルートプロジェクトで管理するようにしてますので基本、CLIのカレントはルートプロジェクトのままで作業することになります。

VSCodeの設定の共有化

.vscodeをルートプロジェクトに置くことでサブプロジェクトでも自動にVSCodeの設定が適応されます。

以下設置しているファイルを箇条書きで説明します。

  • extensions.json
    使用するVSCode拡張の共有
    VSCodeを開いたときに推奨の拡張とかサジェストの表記がされるようになると思います。
  • launch.json
    デバッグの設定を記載
    プロジェクトごとのリモートデバッグによるポートの専有とか防げるんじゃないでしょうか。
  • settings.json
    エディターの設定
    ESLintの有効化や保存時のフォーマッタ設定
    その他モラル的な基本設定とか入れればいいんじゃないでしょうか。
  • xxxxx.code-snippets
    プロジェクトまたがってコピペ的に使いたいの記載すればいいんじゃないでしょうか。

プロジェクトをまたがったStorybookの展開

設定についてこちらになります。

yarn storybook によりコンポーネントのカタログのようなものを閲覧できます。

サブプロジェクトにある xxx.stories.mdx を元に作成されます。

storybook_props

軽くコンポーネントのPropsの検証ができたり

storybook_accessibility

使用しているタグなど実装に関してのアクセシビリティが適切そうかチェックしたり

storybook_performance

レンダリングのパフォーマンス確認などできるようにしています

CIのスケジュールによる全プロジェクトのUT実行

設定についてこちら

それぞれのサブプロジェクトの最新のmasterブランチを持ってきてlint, jestによるテスト実行をするようにしてます。

サブプロジェクト側でも個別にGitHub Actionsによるワークフローを記載しているのでそれぞれのレポジトリのプッシュでもテスト実行するようにしています。

サブプロジェクト: syonet_eight_www

NextJSによるプロジェクトになります。

Serverless Next.js Componentによるデプロイ

NextJSの本家Vercel でもいいんですけどAWSにデプロイ可能なServerless Frameworkを採用しています。

デプロイの仕組みの説明難しいですが serverless.yml の情報がAWSのCloudFormationのテンプレートとして展開されてAWSのそれぞれのサービスをよしなに使って1つのWebサービスを作ってしまうというものです。

下記の図はデプロイで反映するAWSのサービスのイメージになります。

serverless_nextjs

それぞれのAWSのサービスが外部のユーザから見てどう使われているのかのイメージを記載すると

serverless_service

なのをServerless Next.js Componentで再現できちゃうらしいです。
SPA想定だったReactの資産をフル活用できますね。

サブプロジェクト: syonet_eight_design_system

共通のデザインなどをまとめるプロジェクトです。

共通コンポーネントをライブラリ化

yarn build:design_system を実行することでライブラリ化します。

ルートプロジェクトのpackage.jsonにある

"syonet_eight_design_system": "file:./projects/syonet_eight_design_system"

という指定により別のサブプロジェクトでも

import * as DesignSystem from 'syonet_eight_design_system';

で共通のコンポーネントを使用することが可能です。

Figmaで作成したパーツのインポート

yarn import:figma でFigmaで作成したものをインポートするような仕組みを作ってます。

実装したコードはこちらです

https://github.com/igara/syonet_eight_design_system/blob/master/src/scripts/figma.ts

SVG化して使うのは結構安易そうですが、DOMとして扱うのは難しめにみてます。
(だってあいつらNodeIDの順番とか位置関係ようわからんし)

ビルドの設定NextJSのをそのままつかってる

tsconfig.jsonとか.babelrcの設定はNextJSで動かしていたものを使用してます。

syonet_eight_www & syonet_eight_design_system 共通の取り組み

ビジュアルリグレッションテスト実施

reg-suitというGitのプッシュを実行した際にコンポーネントの差分をビジュアライズする仕組みを入れてます。

例えばこんな差分があったときに

https://github.com/igara/syonet_eight_design_system/commit/bf6de6cbd274e239e73f4eb39391bd9b060cb74b

差分としての検知として

reg_suit_top

一覧が表示されて詳細をみようとすると

reg_suit_diff

どのへんに差分があるのかというのをビジュアライズしてくれます。

CIの設定としてこちらになります。

https://github.com/igara/syonet_eight_design_system/blob/master/.github/workflows/vrt.yml

GitHub Actions上では日本語フォントがないので fonts-noto をインストールする必要があったりします。

あとはこちらのテストの結果は現状Slackで通知がくることを確認しています。

slack

CSS in JSとしてemotion採用

好みですが、

import { css } from '@emotion/react';

CSS ModulesぽくもpropsでCSSの値指定できるようないいとこ取りな書き方よくないっすか?

https://github.com/igara/syonet_eight_design_system/blob/master/src/components/icons/menu_icon/menu_icon.styles.ts
https://github.com/igara/syonet_eight_design_system/blob/master/src/components/icons/menu_icon/menu_icon.tsx

サブプロジェクト: syonet_eight_storybook

Storybookを配布するようにserverless.ymlしか置いてないです。

これもServerless Frameworkのコンポーネントをつかっており、websiteというものを使っています。

ただのSPAのサイトをホスティングしたい用途で使うのがちょうど良さそうです。

おわりに

以上自分なりのモノレポ運用についての記載でした。

まだ実装そのものを着手してなく、APIのプロジェクトも作成していない状況なのでこれからすすめていきたいと思います。

アドベントカレンダー、明日は @yuta-ron さんです。
よろしくおねがいします。